;
; Lab7.asm
;
; Author: Evan Zimmerman <zimmermane@msoe.edu>
;         Kevin Duer <duerk@msoe.edu>
; Date: 11/01/12
; Course: CE4920-001
; Instructor: Barnicki
;
; Purpose: Simulates a basic operating system by switching between
;          four processes which output their process number to
;          PORTC. Processes are context switched every 20ms by
;          the Timer1 output compare match interrupt routine.
;          Also includes process information.
;		   Monitors for a power failure event across multiple  
;          processes and uses thewatchdog timer to handle such an event

.include "m32def.inc"

;set up variables for the stack pointer address locations

.equ deg=0x02 ;one degree is equal to 0x02 with my op amp design
.equ p0pid=0x0060 ;   PCB P0 PID
.equ p0pri=0x0061 ;   PCB P0 priority
.equ p0scount=0x0062; PCB P0 switch count
.equ p0spl=0x0063 ;   PCB P0 SPL
.equ p0sph=0x0064 ;   PCB P0 SPH
.equ p1pid=0x0065 ;   PCB P1 PID
.equ p1pri=0x0066 ;   PCB P1 priority
.equ p1scount=0x0067; PCB P1 switch count
.equ p1spl=0x0068 ;   PCB P1 SPL
.equ p1sph=0x0069 ;   PCB P1 SPH
.equ p2pid=0x0070 ;   PCB P2 PID
.equ p2pri=0x0071 ;   PCB P2 priority
.equ p2scount=0x0072; PCB P2 switch count
.equ p2spl=0x0073 ;   PCB P2 SPL
.equ p2sph=0x0074 ;   PCB P2 SPH
.equ p3pid=0x0075 ;   PCB P3 PID
.equ p3pri=0x0076 ;   PCB P3 priority
.equ p3scount=0x0077; PCB P3 switch count
.equ p3spl=0x0078 ;   PCB P3 SPL
.equ p3sph=0x0079 ;   PCB P3 SPH
.equ pcounter=0x0080 ;process counter
.equ tempstor=0x0081 ;place to store reg when pushing/popping sreg
.equ setp=0x0082     ;temperature set point
.equ mute=0x0083     ;sound mute variable
.equ prev0=0x0084    ;previous state of P0 button
.equ prev1=0x0085    ;previous state of P1 button
.equ prev2=0x0086    ;previous state of P2 button


.dseg ; data segment: user RAM
.org 0x0060 ; start of data memory

	.byte 27 ;bytes for addresses defined above

.cseg ; program segment: flash ROM
.org 0x0000 ;vector table address (ref guide page 4)
	rjmp INIT ;reset vector

.org 0x000E ;vector table address (ref guide page 4)
	rjmp OC1AISR ;OCR1A ISR vector jump

.org 0x002A ;first address in program memory

INIT: ;insert your initialization code

;set up the process control block for 
;each process. This consits of storing the high and low 
;half of the stack pointer for each process

	ldi r16,0x00 ;register to compare watchdog reset to
	in r17,MCUCSR 
	andi r17,(1<<WDRF);strip away all bits except watchdog reset
	cpse r16,r17;if r16==r17,then there has been no reset
	rjmp wdogReset;otherwise the watchdog has been reset
	
	ldi r16,((1<<ADEN)|(1<<ADPS2)|(1<<ADPS1))
	out ADCSRA,r16 ;set ADC clock to 125 kHz(64 prescalar),enable ADC

	ldi r16,0xB6  ;80 F in hex, default set point
	sts setp,r16  ;store 80F in set point

	ldi r16,0x00
	sts mute,r16 ;set mute to off
	sts prev0,r16 ;set previous button states as pressed
	sts prev1,r16
	sts prev2,r16

	ldi r16,0x80
	out DDRA,r16 ;set PA7 as output and the rest as input

	ldi r16,0x07
	out PORTA,r16 ;turn on PA0-PA2 pullups

	ldi r16,0x00
	sts p0pid,r16 ;P0 PID=0x00
	ldi r16,0xFF
	sts p0pri,r16 ;P0 priority=0xFF
	ldi r16,0x00
	sts p0scount,r16 ;P0 switch count=0    
	ldi r16,0x01  ;p0 PCB SPH
	sts p0sph,r16
	ldi r16,0x7F  ;p0 PCB SPL
	sts p0spl,r16

	ldi r16,0x01
	sts p1pid,r16 ;P1 PID=1
	ldi r16,0x0F
	sts p1pri,r16 ;P1 priority=0x0F
	ldi r16,0x00
	sts p1scount,r16 ;P1 switch count=0    
	ldi r16,0x01  ;p1 PCB SPH
	sts p1sph,r16
	ldi r16,0xFF  ;p1 PCB SPL
	sts p1spl,r16

	ldi r16,0x02
	sts p2pid,r16 ;P2 PID=2
	ldi r16,0xF0
	sts p2pri,r16 ;P2 priority=0xF0
	ldi r16,0x00
	sts p2scount,r16 ;P2 switch count=0  
	ldi r16,0x02  ;p2 PCB SPH
	sts p2sph,r16
	ldi r16,0x7F  ;p2 PCN SPL
	sts p2spl,r16

	ldi r16,0x03
	sts p3pid,r16 ;P3 PID=3
	ldi r16,0x00
	sts p3pri,r16 ;P3 priority=0
	ldi r16,0x00
	sts p3scount,r16 ;P3 switch count=0  
	ldi r16,0x02  ;p3 PCB SPH
	sts p3sph,r16
	ldi r16,0xFF  ;p3 PCB SPL
	sts p3spl,r16
	

	ldi r16,0x00  ; store/init process counter
	sts pcounter,r0

	lds r16,p0sph ;set stack pointer to p0
	out sph,r16
	lds r16,p0spl
	out spl,r16


;initialize the stacks of each process. This involves pushing the 
;address of the start of each process on that process' stack as well as
;pushing registers with a value of zero to the stack. P0 does not have
;its stack pointer saved atthe end of the init because it will be the first process run
;so it needs to start at the bottom of the stack

	ldi r16,low(P0) ;low adress of P0
	push r16
	ldi r16,high(P0); high address of P0
	push r16
	ldi r16,0x00 ;sreg of P0
	push r16
	push r16 ;push registers
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16

	lds r16,p1sph ;set stack pointer to p1
	out sph,r16
	lds r16,p1spl
	out spl,r16

	;init P1 (same as P0)
	ldi r16,low(P1)
	push r16
	ldi r16,high(P1)
	push r16
	ldi r16,0x00
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16

	in r16,sph ;store p1 stack pointer
	sts p1sph,r16
	in r16,spl
	sts p1spl,r16

	lds r16,p2sph ;set stack pointer to p2
	out sph,r16
	lds r16,p2spl
	out spl,r16

	;init p2 (same as p0)
	ldi r16,low(P2)
	push r16
	ldi r16,high(P2)
	push r16
	ldi r16,0x00
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16

	in r16,sph ;store p2 stack pointer
	sts p2sph,r16
	in r16,spl
	sts p2spl,r16

	lds r16,p3sph ;set stack pointer to p3
	out sph,r16
	lds r16,p3spl
	out spl,r16


	;init p3(same as P0)
	ldi r16,low(P3)
	push r16
	ldi r16,high(P3)
	push r16
	ldi r16,0x00
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16

	in r16,sph ;store p3 stack pointer
	sts p3sph,r16
	in r16,spl
	sts p3spl,r16


	;initialize the Timer1 interrupt for a 20ms 
	;context switch between processes
	;16MHz clock with prescaler of 256 = 62.5KHz
	;1/62.5KHz = 16us/tick
	;20ms/16us = 1250 ticks (0x04E2) (OC1A value)
	ldi r16,0x04  
	out OCR1AH,r16       ;load 0x04E2 as compare value
	ldi r16,0xE2
	out OCR1AL,r16

	in r16,TIMSK ;turn on interrupt for OCR1A compare match
	ori r16,(1<<OCIE1A)
	out TIMSK,r16 

	ldi r16,0x00;clear TCNT1 val
	out TCNT1H,r16
	out TCNT1L,r16

	sei ;enable interrupts

	;set up Timer/counter 1 for CTC mode and a prescalar of 8
	;also starts the timer

	in r16,TCCR1B
	ori r16,(1<<WGM12)|(1<<CS11)
	out TCCR1B,r16


	ldi r16,0xFF
	out DDRC,r16 ;set portc as output
	lds r16,p0pid ;load pid and output it
	out PORTC,r16
	lds r16,p0pri ;load priority and output it
	out PORTC,r16
	lds r16,p0scount ;load switch count and output it
	inc r16       ;update switch count and store it
	sts p0scount,r16 
	out PORTC,r16    
	lds r16,p0sph ;set stack pointer to p0
	out PORTC,r16 ; p0sph to scope
	out sph,r16
	lds r16,p0spl
	out PORTC,r16
	;p0spl to scope
	out spl,r16

	in r16,WDTCR
	ori r16,((1<<WDP2)|(1<<WDP0)|(1<<WDE)) ;set watchdog for 0.5s timeout and enable it
	out WDTCR,r16


	rjmp P0 ;go to process zero



OC1AISR: 

	cli
	sts tempstor,r0 ;;store r0 in a temporary location as it will be used to read in the sreg
    in r0,sreg ;save the sreg of the process
	push r0
	lds r0,tempstor ;restore r0
	push r0 ;push all registers
	push r1
	push r2
	push r3
	push r4
	push r5
	push r6
	push r7
	push r8
	push r9
	push r10
	push r11
	push r12
	push r13
	push r14
	push r15
	push r16
	push r17
	push r18
	push r19
	push r20
	push r21
	push r22
	push r23
	push r24
	push r25
	push r26
	push r27
	push r28
	push r29
	push r30
	push r31
	wdr ;reset watchdog

	lds r16,pcounter;determine which process to switch to
	inc r16         ;by setting the process counter to the next process
	sts pcounter,r16
	cpi r16,0
	breq startP0
	cpi r16,1
	breq startP1
	cpi r16,2
	breq startP2
	cpi r16,3
	breq startP3stub ;needed as breq is out of range for startP3

startP3stub:
	rjmp startP3 ;see above

startP0:

	ldi r16,0xFF
	out DDRC,r16 ;set portc as output
	lds r16,p0pid ;load pid and output it
	out PORTC,r16
	lds r16,p0pri ;load priority and output it
	out PORTC,r16
	lds r16,p0scount ;load switch count and output it
	inc r16       ;update switch count and store it
	sts p0scount,r16 
	out PORTC,r16
	in r16,sph ;store previous process' stack pointer
	sts p3sph,r16
	in r16,spl
	sts p3spl,r16
	lds r16,p0sph ;load new process' stack pointer
	out PORTC,r16 ;output SPH to scope
	out sph,r16
	lds r16,p0spl
	out PORTC,r16 ;output SPL to scope
	out spl,r16
	rjmp startproc ;start the process
	

startP1:

	;same process as startP0
	ldi r16,0xFF
	out DDRC,r16 ;set portc as output
	lds r16,p1pid ;load pid and output it
	out PORTC,r16
	lds r16,p1pri ;load priority and output it
	out PORTC,r16
	lds r16,p1scount ;load switch count and output it
	inc r16       ;update switch count and store it
	sts p1scount,r16 
	out PORTC,r16
	in r16,sph
	sts p0sph,r16
	in r16,spl
	sts p0spl,r16
	lds r16,p1sph
	out PORTC,r16 ;output SPH to scope
	out sph,r16
	lds r16,p1spl
	out PORTC,r16 ;output SPL to scope
	out spl,r16
	rjmp startproc
	

startP2:

	;same process as startP0
	ldi r16,0xFF
	out DDRC,r16 ;set portc as output
	lds r16,p2pid ;load pid and output it
	out PORTC,r16
	lds r16,p2pri ;load priority and output it
	out PORTC,r16
	lds r16,p2scount ;load switch count and output it
	inc r16       ;update switch count and store it
	sts p2scount,r16 
	out PORTC,r16
	in r16,sph
	sts p1sph,r16
	in r16,spl
	sts p1spl,r16
	lds r16,p2sph
	out PORTC,r16 ;output SPH to scope
	out sph,r16
	lds r16,p2spl
	out PORTC,r16 ;output SPL to scope
	out spl,r16
	rjmp startproc
	

startP3:

	;same process as startP0
	ldi r16,0xFF
	out DDRC,r16 ;set portc as output
	lds r16,p3pid ;load pid and output it
	out PORTC,r16
	lds r16,p3pri ;load priority and output it
	out PORTC,r16
	lds r16,p3scount ;load switch count and output it
	inc r16       ;update switch count and store it
	sts p3scount,r16 
	out PORTC,r16
	lds r16,pcounter ;store 0xFF in the process counter 
	ldi r16,0xFF     ;so that when it is next incremented it will be set at 0
	sts pcounter,r16
	in r16,sph ;same process as startP0
	sts p2sph,r16
	in r16,spl
	sts p2spl,r16
	lds r16,p3sph
	out PORTC,r16 ;output SPH to scope
	out sph,r16
	lds r16,p3spl
	out PORTC,r16 ;output SPL to scope
	out spl,r16
	rjmp startproc

startproc:

	pop r31 ;restore all registers for the process
	pop r30
	pop r29
	pop r28
	pop r27
	pop r26
	pop r25
	pop r24
	pop r23
	pop r22
	pop r21
	pop r20
	pop r19
	pop r18
	pop r17
	pop r16
	pop r15
	pop r14
	pop r13
	pop r12
	pop r11
	pop r10
	pop r9
	pop r8
	pop r7
	pop r6
	pop r5
	pop r4
	pop r3
	pop r2 
	pop r1
	pop r0
	sts tempstor,r0 ;store r0 sp that the sreg can be restored
	pop r0 ;restore sreg
	out sreg,r0
	lds r0,tempstor ;restore r0
	reti ;return from the interrupt

P0: 
	ldi r16,0xFF ;print process number
	out DDRC,r16 ;on portC by setting DDRC as output
	ldi r16,0x00 ;and then outputting the process number
	out PORTC,r16

	sbic PINA,0;if the mute button is not pressed, don't do anything
	rjmp noMutePress
	lds r16,prev0;compare to the previous button state
	cpi r16,0x00
	breq soundMute ;if the previous state was not pressed,
                   ;then mute/umute the sound

muteStillPressed:
	rjmp P0 ;otherwise the button is still pressed

noMutePress:
	ldi r16,0x00   ;button not pressed, so store previous state as not pressed
	sts prev0,r16
	lds r16,mute
	out portb,r16  
	rjmp P0        
                   
soundMute:
	ldi r16,0x01
	sts prev0,r16 ;previous state is pressed
	lds r16,mute ;load the mute flag
	cpi r16,0x00 ;compare to mute off
	brne muteOff ;if not equal ,then mute must be turned off
	ldi r16,0x01 ;otherwise turn mute on
	sts mute,r16 ;store the mute flag
	rjmp P0

muteOff:
	ldi r16,0x00 ;otherwise turn mute on
	sts mute,r16 ;store the mute flag
	ldi r16,0x87  
	out PORTA,r16;chirp to indicate mute disabled
	rjmp P0

P1:

	ldi r16,0xFF ;same as P0
	out DDRC,r16
	ldi r16,0x01
	out PORTC,r16

	sbic PINA,1
	rjmp noIncPress ;if the inc set point button is not pressed, then don't do so
	lds r16,prev1;compare to the previous button state
	cpi r16,0x00
	breq incSetp ;if the previous state was not pressed,
                 ;then increase the set point

incStillPressed:
	ldi r16,0x01;otherwise the button is still pressed
	sts prev1,r16;so previous state is pressed
	rjmp P1



noIncPress:
	ldi r16,0x00 ;button was not pressed. so previous state is unpressed
	sts prev1,r16 
	rjmp P1       
                   
incSetp:
	ldi r16,0x01  
	sts prev1,r16 ;set previous state to pressed
	lds r16,setp ;load set point
	ldi r17,0xCC ;don't go above 90 F
	cpse r16,r17 ;don't go above 90 F
	subi r16,-deg ;add 1 degree (0x02) to the set point ((-)-=add)
	sts setp,r16;store the set point
	rjmp P1

P2: 

	ldi r16,0xFF ;same as P0
	out DDRC,r16
	ldi r16,0x02
	out PORTC,r16

	sbic PINA,2;if PINA bit 2 is clear,then it is pressed
	rjmp noDecPress ;if the dec set point button is not pressed, then don't do so
	lds r16,prev2;compare to the previous button state
	cpi r16,0x00
	breq decSetp ;if the previous state was not pressed,
                 ;then decrease the set point

decStillPressed:
	ldi r16,0x01 ;otherwise the button is still pressed
	sts prev2,r16
	rjmp P2

noDecPress:
	ldi r16,0x00  ;previous state is unpressed 
	sts prev2,r16  
	rjmp P2        
                   
decSetp:
	ldi r16,0x01 
	ldi r17,0x00 ;don't go below 0
	sts prev2,r16 ;set previous state to pressed
	lds r16,setp ;load set point
	cpse r16,r17 ;don't go below 0
	subi r16,deg ;suv 1 degree (0x02) to the set point 
	sts setp,r16;store the set point
	rjmp P2



P3: 

	ldi r16,0xFF ;same as P0
	out DDRC,r16
	;ldi r16,0x1A
	;out PORTC,r16

	ldi r16,((1<<ADLAR)|(1<<MUX2)|(1<<MUX0)|(1<<REFS0));enable ADC left adjust,
	out ADMUX,r16							;take samples from ADC5,AVCC is reference

	sbi ADCSRA,ADSC;start ADC conversion

convWaitP3:
	sbic ADCSRA,ADSC ;wait for ADC conversion to finish
	rjmp convWaitP3

	in r16,ADCH ;get ADC result from ADC5
	out PORTC,r16;output ADC result

	mov r17,r16 ;store the ADC result in r17
	lds r16,setp;load temp set point
	out PORTC,r16;output temp set point
	mov r16,r17;move ADC result back to r16
	
	lds r17,setp;load temp set point to r17
	cp r17,r16 ;compare ADC input to temperature set point
	brsh noAlert ; if the set point is >= the current temp, don't worry
	ldi r16,0x87 ;otherwise sound sonic alert
	lds r17,mute;only alert if mute flag is set to zero
	cpi r17,0x00
	brne noAlert
	out PORTA,r16
	rjmp P3

noAlert:

	ldi r16,0x07
	out PORTA,r16
	rjmp P3
	
wdogReset:
	in r16,MCUCSR ;clear watchdog reset flag
	andi r16,~(1<<WDRF)
	out MCUCSR,r16
	ldi r16,0xFF ;make port c an output port
	out DDRC,r16
Repeat:
	ldi r16,0xAA ;output 0xFE on portc
	out PORTC,r16
	ldi r16,0x55 ;output 0xFE on portc
	out PORTC,r16
	ldi r16,0xAA ;output 0xFE on portc
	out PORTC,r16
	ldi r16,0x55 ;output 0xFE on portc
	out PORTC,r16
	rjmp Repeat